/**
 * 
 */
package com.gragra.sampling;
import it.unimi.dsi.fastutil.ints.IntIterator;
import com.gragra.util.OpenUnitElementCounter;
import com.gragra.util.OpenUnitElementCounter.MappingIterator;
/**
 * @author Christoph Teichmann
 * 30.09.2013
 */
public abstract class CRPProcess implements PopulationProbabilityAssigner
{
	/**
	 * 
	 */
	private final int population;
	/**
	 * 
	 */
	private final OpenUnitElementCounter[] given;
	/**
	 * 
	 * @param population
	 */
	public CRPProcess(int population)
	{
		this.population = population;
		given = new OpenUnitElementCounter[population];
		for(int i=0;i<population;++i)
		{given[i] = new OpenUnitElementCounter();}
	}
	/* (non-Javadoc)
	 * @see com.gragra.sampling.PopulationProbabilityAssigner#getPopulationSize()
	 */
	@Override
	public int getPopulationSize()
	{return population;}
	/**
	 * 
	 * @param unit
	 * @param element
	 * @param by
	 * @param thread
	 */
	public void changeGiven(int unit,int element,double by, int thread)
	{this.given[thread].add(unit, element, by);}
	/* (non-Javadoc)
	 * @see com.gragra.sampling.PopulationProbabilityAssigner#getNonLocalLogLike(com.gragra.sampling.PopulationSamplingStructure, int)
	 */
	@Override
	public double getNonLocalLogLike(int num)
	{
		MappingIterator mi = this.given[num].getIterator();
		double ret = 0.0;
		while(mi.hasNext())
		{
			mi.setToNext();
			int unit = mi.getCurrentUnit();
			int element = mi.getCurrentElement();
			int amount = (int) mi.getCurrentValue();
			LogarithmicFactorialCash els = getElementCash(unit,element);
			if(amount > 0)
			{ret += els.getValue(amount-1, num);}
		}
		IntIterator iit = this.given[num].getUnitIterator();
		while(iit.hasNext())
		{
			int unit = iit.nextInt();
			int amount = (int) this.given[num].getSum(unit);
			LogarithmicFactorialCash uns = getUnitCash(unit);
			if(amount > 0)
			{ret -= uns.getValue(amount-1, num);}
		}
		return ret;
	}
	/**
	 * 
	 * @param unit
	 * @return
	 */
	protected abstract LogarithmicFactorialCash getUnitCash(int unit);
	/**
	 * 
	 * @param unit
	 * @param element
	 * @return
	 */
	protected abstract LogarithmicFactorialCash getElementCash(int unit, int element);
	/**
	 * 
	 * @param oue
	 * @param thread
	 */
	protected double makeLogProb(OpenUnitElementCounter oue, int thread)
	{
		MappingIterator mi = oue.getIterator();
		OpenUnitElementCounter give = this.given[thread];
		double ret = 0.0;
		while(mi.hasNext())
		{
			mi.setToNext();
			int unit = mi.getCurrentUnit();
			int element = mi.getCurrentElement();
			int amount = (int) mi.getCurrentValue();
			int given = (int) give.getValue(unit, element);
			LogarithmicFactorialCash els = getElementCash(unit,element);
			ret += els.getValue(amount+given-1, thread);
			if(given > 0)
			{ret -= els.getValue(given-1, thread);}
		}
		IntIterator iit = oue.getUnitIterator();
		while(iit.hasNext())
		{
			int unit = iit.nextInt();
			int amount = (int) oue.getSum(unit);
			int given = (int) give.getSum(unit);
			LogarithmicFactorialCash uns = getUnitCash(unit);
			ret -= uns.getValue(amount+given-1, thread);
			if(given > 0)
			{ret += uns.getValue(given-1, thread);}
		}
		return ret;
	}
	/**
	 * 
	 * @param thread
	 * @return
	 */
	protected OpenUnitElementCounter getGiven(int thread)
	{return this.given[thread];}
}